// ------------------------------------------------------------------------ //
// Texas A&M University                                                     //
// CPSC321 Computer Architecture; Fall 2002                                 //
//                                                                          //
// $Id: ALU_behav.v,v 1.3 2002/11/14 16:06:04 miket Exp miket $             //
//                                                                          //
// ------------------------------------------------------------------------ //

// ------------------------------------------------------------------------ //
// Behavioral Verilog Module for a MIPS-like ALU  //
// -- In Continuous and procedure assignments Verilog extends the smaller //
// operands by replicating their MSbit, if it is equal to x, z; otherwise //
// operends them with 0's. Arithmetic is interpreted as 2's C //
// -- regs are considered as unsigned bit-vectors but the all arithmetic  //
// is done in 2's complement. //
// // At ALU Instatiation time parameter n determines the ALU bit-size //
// ------------------------------------------------------------------------ //

// repetoire of operations for ALU, selected by ALU_ctr  //
`define SLL  6'b000000 // shift left
`define LUI  6'b000001 // Shift left 16 for Load Upper Immediate
`define SRL  6'b000010 // shift logical right (fill with 0's)
`define SRA  6'b000011 // shift arithmetic right (sign extend)
`define JR   6'b001000 // Jump Register
`define ADD  6'b100000 // 2's compl add
`define ADDU 6'b100001 // unsigned add
`define SUB  6'b100010 // 2's compl subtract
`define SUBU 6'b100011 // unsigned subtract
`define AND  6'b100100 // bitwise AND
`define OR   6'b100101 // bitwise OR
`define XOR  6'b100110 // bitwise XOR
`define SLT  6'b101010 // set result=1 if less than 2's compl
`define SLTU 6'b101011 // set result=1 if less than unsigned
`define NOP  6'b111111 // do nothing



module ALU_behav( ADin, BDin, ALU_ctr, Shamt, Result, Zero ); 

   parameter n = 32, Ctr_size = 6;
	 parameter T_cal = 5;

   input [Ctr_size-1:0] ALU_ctr;
   input [n-1:0] 	ADin, BDin;
	 input [4:0] Shamt;
   output [n-1:0] 	Result;
   reg Carry_In;
   reg [n-1:0] 	Result, tmp;
   output 		Zero;
   reg 			Carry_out, Overflow, Zero;

   always @(ALU_ctr or ADin or BDin)
      begin
      Carry_In = 0;
	 case(ALU_ctr)
	   `SLL:  #T_cal Result = (BDin << Shamt);
		 `LUI:  #T_cal Result = (BDin << 16);
     `SRL:  #T_cal Result = (BDin >> Shamt);
`SRA:  #T_cal Result = ($signed(BDin) >>> Shamt);
     //`SRA:  #T_cal Result = {{Shamt{BDin[31]}}, {BDin >> Shamt}};
		 `JR:   #T_cal Result = ADin;
	   `ADD:  begin
	      #T_cal {Carry_out, Result} = ADin + BDin;
	      #T_cal Overflow = ADin[n-1] & BDin[n-1] & ~Result[n-1]
			 | ~ADin[n-1] & ~BDin[n-1] & Result[n-1];
	   end
	   `ADDU: #T_cal {Overflow, Result} = ADin + BDin;
	   `SUB:  begin
	      #T_cal {Carry_out, Result} = ADin - BDin;
	      #T_cal Overflow = ADin[n-1] & ~BDin[n-1] & Result[n-1]
			 | ~ADin[n-1] & BDin[n-1] & ~Result[n-1];
	   end
	   `SUBU: #T_cal {Overflow, Result} = ADin - BDin;
	   `SLT:  begin
	      #T_cal {Carry_out, tmp} = ADin - BDin;
	      #T_cal Overflow = ADin[n-1] & ~BDin[n-1] & ~tmp[n-1] 
			 | ~ADin[n-1] & BDin[n-1] & tmp[n-1];
	      #T_cal Result = tmp[n-1] ^ Overflow;

	   end
	   `SLTU: begin
	      #T_cal {Carry_out, tmp} = ADin - BDin;
	      #T_cal Result = Carry_out;
	   end
	   `OR :  #T_cal Result = ADin | BDin;
	   `AND:  #T_cal Result = ADin & BDin;
	   `XOR:  #T_cal Result = ADin ^ BDin;
	   `NOP:  #T_cal Result = ADin;
	 endcase
	 
 end

	 always@(Result)
	    #T_cal Zero = ~| Result;  // Result = 32'b0
endmodule
/*
module TestALU;

   parameter n = 32, Ctr_size = 4;

   reg [n-1:0] A, B, T;
   wire [n-1:0]  R, tt;
   reg 	       Carry_in;
   wire        Carry_out, Overflow, Zero;
   
   reg [Ctr_size-1:0] ALU_ctr;

   integer 	      num;
   
   ALU_behav ALU( A, B, ALU_ctr, R, Overflow, Carry_in, Carry_out, Zero );
   
   always @( R or Carry_out or Overflow or Zero )
      begin
	 $display("%0d\tA = %0d B = %0d; R = %0d; Cout = %b Ovfl = %b Zero = %b OP = %b n = %d",  $time, A, B, R, Carry_out, Overflow, Zero, ALU_ctr, num );  
	 num = num + 1;
      end
   
   initial begin
      #0 num = 0; Carry_in = 0; 
      #1 A = 101; B = 0; ALU_ctr = `NOP;
      #10 A = 10; B = 10; ALU_ctr = `ADD;
      #10 A = 10; B = 20; ALU_ctr = `ADDU;
      #10 A = 10; B = 20; ALU_ctr = `SLT;
      #10 A = 10; B = 20; ALU_ctr = `SLTU;
      #10 A = 32'hffffffff; B = 1; ALU_ctr = `ADDU;
      #10 A = 10; B = 10; ALU_ctr = `ADDU;
      #10 A = 10; B = 10; ALU_ctr = `SUB;
      #10 A = 1; B = 1; ALU_ctr = `SUBU;
      #10 A = 10; B = 10; ALU_ctr = `SUB;
      #10 A = 10; B = 10; ALU_ctr = `SUBU;
      #10 A = -13; B = -12; ALU_ctr = `SLT;
      #100 $finish;
   end
endmodule */
